﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;

namespace SetNet
{
    abstract public class PeerBase
    {
        Socket _socket = null;
        Thread receive;
        bool socketIsWorking = true;
        byte[] buffer;
        Serialization serialize;

        //for UDP
        Server server;
        Timer timer;
        int to_check_timeout;
        public EndPoint ePoint;

        public PeerBase(ClientInfo info)
        {
            buffer = new byte[Configuration.bufferLenght];
            if (Configuration.type == (byte)Configuration.socketType.tcp)
            {
                
                _socket = info.socket;
                _socket.NoDelay = Configuration.noDelay;
                receive = new Thread(StartReceive);
                receive.IsBackground = true;
                receive.Start();
                socketIsWorking = true;
            }
            if (Configuration.type == (byte)Configuration.socketType.udp)
            {
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                ePoint = info.endPoint;
                server = info.server;
                _socket.Connect(ePoint);
                socketIsWorking = true;
                to_check_timeout = 0;
                timer = new Timer(CheckTimeOut);
                timer.Change(0, 1000);
                Server.AddClient(this);
            }
        }


        void StartReceive()
        {
            while (_socket.Connected)
            {
                int read = 0;
                if (Configuration.type == (byte)Configuration.socketType.tcp && socketIsWorking)
                {

                    try
                    {
                        read = _socket.Receive(buffer);
                        if (read == 0)
                        {
                            Disconnect();
                        }
                    }
                    catch (SocketException ex) { Disconnect(); }        
                }

                if (read > 0 && socketIsWorking)
                {
                    EventData data = Serialization.Deserializate(buffer);

                    if (data != null)
                    {
                        ReceiveData(data);
                    }
                }
                else if (read <= 0 && socketIsWorking)
                {
                    if (socketIsWorking)
                    {
                        socketIsWorking = false;
                        Disconnect();
                    }
                    return;
                }
                

            }
        }

        public void Disconnect()
        {
            if (socketIsWorking)
            {
                ClientDisconected();
                Console.WriteLine("Client Disconnected");
                
                if (Configuration.type == (byte)Configuration.socketType.tcp)
                {
                    receive.Abort();
                    receive.Join();
                }
                if (Configuration.type == (byte)Configuration.socketType.udp)
                {
                    timer.Dispose();
                }

                _socket.Shutdown(SocketShutdown.Both);
                _socket.Close();
                server.Removeclient(this);
                socketIsWorking = false;
            }
        }

        public void UpdTimeOutChecker()
        {
            to_check_timeout = 0;
        }

        public void SendEvent(EventData eventData)
        {

                if (_socket.Connected)
                {
                    if (Configuration.type == (byte)Configuration.socketType.tcp)
                    {
                        byte[] byteData = Serialization.Serializate(eventData);
                        _socket.Send(byteData, 0, byteData.Length, 0);
                    }
                    else if (Configuration.type == (byte)Configuration.socketType.udp)
                    {
                        byte[] byteData = Serialization.Serializate(eventData);
                        server.SendUDP(byteData, ePoint);
                    }
                }
            
        }

        private void CheckTimeOut(object state)
        {
            if (to_check_timeout >= Configuration.clientTimeOut)
            {
                Disconnect();
            }
            to_check_timeout++;
        }

        abstract public void ReceiveData(EventData data);

        abstract public void ClientDisconected(); 
    }
}
